登录 token 生成源码解析
sequenceDiagram
participant User as User
participant UI as pigx-ui
participant Gateway as pigx-gateway
participant Auth as pigx-auth
participant UPMS as pigx-upms
User->>UI: 提交登录请求
UI->>Gateway: 加密报文
Gateway->>Auth: 路由转发请求
Auth->>Auth: 请求验证码校验
Auth->>Auth: 解密请求报文
Auth->>UPMS: 查询用户信息
UPMS-->>Auth: 返回用户数据
Auth-->>Gateway: 返回 token
Gateway-->>UI: 返回 token
登录页面
前端账号密码登录入口文件位于 src/views/login/component/password.vue,此文件涉及页面验证码获取逻辑,行为验证码基于 AJ-Captcha 实现。
密码登录页面逻辑
前端登录成功后跳转逻辑
前端报文加密
💡报文加密说明
为了保证前端提交登录信息(尤其是密码字段)在传输过程中不被抓包破解,PIGX 框架对登录报文进行了对称加密处理,详细说明参考:前端报文加密的业务。
加密示例:
password 明文:123456
转成
password 密文:JFat0Zdc
前端提供简单的 AES 对称加密算法,注意 key 和后端网关配置相同,这里打包混淆后,相对安全。
网关请求转发
💡路由转发核心功能
网关中最重要的功能是路由转发,根据请求前缀匹配到对应服务。例如所有以 /auth 开头的请求会自动转发至 pigx-auth 服务的接口处理。
以下为 sys_route_conf 表定义的路由规则:
⚠路由前缀截取
在 PigxRequestGlobalFilter 过滤器中另外一个重要的功能就是截取路由前缀。
示例:
http://127.0.0.1:9999/auth/oauth2/token
转发到 pigx-auth 的请求路径自动截取前缀变成
http://127.0.0.1:3000/oauth2/token
验证码校验处理
pigx-auth 认证中心在接收到网关转发的请求后,会通过 ValidateCodeFilter 对验证码(图形、短信)等进行校验。
前端请求密文解密
💡密文解密处理
pigx-auth 在处理完验证码判断逻辑后,PasswordDecoderFilter 针对 /oauth2/token 请求会进行前端密码解密。
解密示例:
password 密文:JFat0Zdc
转成
password 明文:123456
auth 模块详解
登录请求报文示例:
POST /auth/oauth2/token?grant_type=password&scope=server HTTP/1.1
Host: pig-gateway:9999
Authorization: Basic dGVzdDp0ZXN0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
username=admin&password=YehdBPev
客户端认证
在登录请求中会携带 Basic base64(clientId:clientSecret),OAuth2ClientAuthenticationFilter 会通过调用 RegisteredClientRepository(数据库存储)来判断传入的客户端是否正确。
接收登录请求
OAuth2TokenEndpointFilter 会接收通过上文 OAuth2ClientAuthenticationFilter 客户端认证的请求。
组装认证对象
AuthenticationConverter 会根据请求中的参数和授权类型组装成对应的授权认证对象。
登录认证对象
AuthenticationToken 继承关系:
public class XXXAuthenticationToken extends OAuth2ResourceOwnerBaseAuthenticationToken {
}
授权认证调用
核心认证逻辑
多用户体系匹配 UserDetailsService
密码匹配校验
用户状态校验
用户查询逻辑
用户查询逻辑的多种实现形式:
- 解耦:通过 feign 查询其他系统获取并组装成
UserDetails
- 简单:认证中心直接查询 DB 并组装成
UserDetails
密码校验逻辑
💡密码加密方式
PasswordEncoder 会自动根据特征码匹配对应的加密算法,因此在查询用户对象组装成 UserDetails 时需要添加加密特征码前缀。
默认支持加密方式:
代码示例:
return new UserDetails(user.getUsername(), "{bcrypt}" + "数据库存储的密文");
生成 OAuth2AccessToken
Token 存储持久化
💡存储方式扩展
当前 Spring Authorization Server 仅支持 JDBC 和内存存储,PIGX 扩展支持 Redis 实现。
登录成功事件处理
✓事件驱动扩展
基于 SpringEvent 事件处理,可以在这里做更多的处理,如日志记录、个性化配置等。
请求结果输出 Token
private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication;
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
// 无状态 注意删除 context 上下文的信息
SecurityContextHolder.clearContext();
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
}
定义具体的输出返回格式等逻辑:
